package middleware

import (
	"errors"
	"fmt"
	"gitee.com/wuzheng0709/backend-gopkg/infrastructure/pkg/code"
	"gitee.com/wuzheng0709/backend-gopkg/infrastructure/pkg/gin/log"
	"gitee.com/wuzheng0709/backend-gopkg/infrastructure/pkg/jwt"
	"github.com/gin-gonic/gin"
	"github.com/yudeguang/ratelimit"
	"time"
)

// 步骤一：初始化
var UrlLimit LimitObj
var UserLimit []LimitObj
var IpLimit []LimitObj
var AllUrls []string

type LimitObj struct {
	Item      string          `json:"item"`
	LimitRule *ratelimit.Rule `json:"limit_rule"`
}

func RateLimit(skipper ...SkipperFunc) gin.HandlerFunc {
	return func(c *gin.Context) {
		if len(skipper) > 0 && skipper[0](c) {
			c.Next()
			return
		}
		token := c.Request.Header.Get("token")
		claims, err3 := jwt.ParseTokenFromRedis(token)

		obj := c.Request.URL.Path
		Ip := GetRealIp(c)
		log.Info("cccc", Ip)
		err := CheckUrlLimit(UrlLimit.LimitRule, obj)
		if err != nil {
			c.JSON(400, gin.H{
				"code": code.TooManyRequests,
				"msg":  "当前访问人数较多,请稍后访问",
			})
			c.Abort()
			return
		}
		if err3 == nil && claims.UserId != "" {
			limit, err := CheckUserOrIpLimit(UserLimit, claims.UserId, obj)
			if limit.Item == "" {
				limit.Item = claims.UserId
				limit.LimitRule = ratelimit.NewRule()
				limit.LimitRule.AddRule(time.Second*10, 500)
				UserLimit = append(UserLimit, limit)
			}
			if err != nil {
				c.JSON(400, gin.H{
					"code": code.TooManyRequests,
					"msg":  "您的访问次数较为频繁,请稍后再试",
				})
				c.Abort()
				return
			}
		} else if Ip != "" {
			limit, err := CheckUserOrIpLimit(IpLimit, Ip, obj)
			if limit.Item == "" {
				limit.Item = Ip
				limit.LimitRule = ratelimit.NewRule()
				limit.LimitRule.AddRule(time.Second*10, 500)
				IpLimit = append(IpLimit, limit)
			}
			if err != nil {
				c.JSON(400, gin.H{
					"code": code.TooManyRequests,
					"msg":  "您的访问次数较为频繁,请稍后再试",
				})
				c.Abort()
				return
			}
		}

	}
}

// 模拟用户访问并打印
func Init(urls []string) {
	AllUrls = urls
	UrlLimit.LimitRule = ratelimit.NewRule()
	UrlLimit.LimitRule.AddRule(time.Second*1, 200) //每1秒只允许访问200次

	//步骤二：增加一条或者多条规则组成复合规则，规则必须至少包含一条规则

	//r.AddRule(time.Minute*30, 50) //每30分钟只允许访问50次
	//r.AddRule(time.Hour*24, 500)  //每天只允许访问500次
	//步骤三：调用函数判断某用户是否允许访问
	/*
	   allow:= r.AllowVisit(user)
	*/

}

func CheckUserOrIpLimit(limitList []LimitObj, item string, url string) (limit LimitObj, err error) {
	fmt.Println("\r\n以下用户开始访问:", item)
	for i := 0; i < len(limitList); i++ {
		if limitList[i].Item == item {
			limit = limitList[i]
			break

		}
	}
	if limit.Item != "" {
		if limit.LimitRule.AllowVisit(url) {
			log.Info(item, "访问1次,剩余:", limit.LimitRule.RemainingVisits(url))
		} else {
			errmsg := "您的访问次数较为频繁,请稍后再试"
			err = errors.New(errmsg)
		}
	}

	return limit, err
}
func CheckUrlLimit(r *ratelimit.Rule, item string) (err error) {
	fmt.Println("\r\n以下用户开始访问:", item)
	if r.AllowVisit(item) {
		log.Info(item, "访问1次,剩余:", r.RemainingVisits(item))
	} else {
		errmsg := "当前访问人数较多,请稍后再试"
		err = errors.New(errmsg)
	}
	return
}

func ManualEmptyVisitorRecord(c *gin.Context) {
	for j := 0; j < len(AllUrls); j++ {
		UrlLimit.LimitRule.ManualEmptyVisitorRecordsOf(AllUrls[j])
		for i := 0; i < len(UserLimit); i++ {
			UserLimit[i].LimitRule.ManualEmptyVisitorRecordsOf(AllUrls[j])

		}
		for i := 0; i < len(IpLimit); i++ {
			IpLimit[i].LimitRule.ManualEmptyVisitorRecordsOf(AllUrls[j])
		}
	}

	return
}
